package com.gadgets.busi.fileload;

import com.gadgets.busi.bm02.common.GadgetsBm02Constant;
import com.gadgets.busi.bm02.common.GadgetsBm02Utils;
import com.gadgets.busi.bm02.dto.MaterialDictQueryDTO;
import com.gadgets.busi.bm02.dto.RecipeQueryDTO;
import com.gadgets.busi.bm02.pojo.MaterialDict;
import com.gadgets.busi.bm02.pojo.MaterialRecipe;
import com.gadgets.busi.bm02.repository.IMaterialDictRepository;
import com.gadgets.busi.bm02.repository.IMaterialRecipeRepository;
import com.gadgets.framework.common.constant.GadgetsConstant;
import com.gadgets.framework.common.exception.GadgetsValidateException;
import com.gadgets.framework.common.util.Dom4jUtil;
import com.gadgets.framework.common.util.PinYinUtil;
import com.gadgets.framework.common.util.StringUtils;
import com.gadgets.framework.fileload.AbstractFileScanLoad;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author UncleXiu
 * @version V1.0.0
 * @data 2020-11-27
 * 
 */
@Component
public class MaterialConfigScanLoad extends AbstractFileScanLoad {

    public MaterialConfigScanLoad() {
        super(".material.xml");
    }

    @Autowired
    private IMaterialDictRepository dictRepository;
    @Autowired
    private IMaterialRecipeRepository recipeRepository;
    @Autowired
    private GadgetsBm02Utils gadgetsBm02Utils;

    private final List<Element> unOriMaterialList = new ArrayList<>();

    private final HashSet<String> materialSet = new HashSet<>();

    /**
     * 分析文件（压缩文件）
     *
     * @param targetFile 目标文件
     * @param entry      文件内容
     */
    @Override
    public void analysisFile(ZipFile targetFile, ZipEntry entry) {
        try {
            InputStream inputStream = targetFile.getInputStream(entry);
            Document document = Dom4jUtil.readDomByStream(inputStream);
            analysisDom(document);
        } catch (IOException | DocumentException e) {
            logger.error("打开文件{}!{}的读取流失败，失败原因{}",targetFile.getName(),entry.getName(),e.getMessage());
            logger.error(StringUtils.formatExceptionStack(e));
        }
    }

    /**
     * 分析文件（非压缩文件）
     *
     * @param file 目标文件
     */
    @Override
    public void analysisFile(File file) {
        try {
            Document document =Dom4jUtil.readDomByFile(file);
            analysisDom(document);
        } catch (DocumentException e) {
            logger.error("打开文件{}的读取流失败，失败原因{}",file.getName(),e.getMessage());
            logger.error(StringUtils.formatExceptionStack(e));
        }
    }

    /**
     * 最终文件处理
     */
    @Override
    public void analysisFinally() {
        // 保存合成表
        analysisRecipe();
        // 清空物品字典
        materialSet.clear();
    }

    /**
     * 文本解析
     * @param recipeDocument    合成表
     */
    protected void analysisDom(Document recipeDocument){
        Element rootElement = recipeDocument.getRootElement();
        String modName = rootElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIALS_MOD);
        List<Element> materialInfo = recipeDocument.getRootElement().elements();
        MaterialDictQueryDTO queryDTO = new MaterialDictQueryDTO();
        if(null!=materialInfo){
            for (Element element : materialInfo) {
                // 默认不存在当前物品字典
                boolean dictExistFlag;
                String name = element.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_NAME);
                if(StringUtils.isBlank(name)){
                    throw new GadgetsValidateException("物品名不能为空！请检查配置文件");
                }
                queryDTO.setMaterialName(name);
                queryDTO.setModName(modName);
                List<MaterialDict> dictedList = dictRepository.selectAllMaterialDictByCondition(queryDTO);
                dictExistFlag = (null!=dictedList && dictedList.size()>0);
                MaterialDict dict = null;
                if(dictExistFlag){
                    logger.trace("物品：{}已经存在，无需新增字典",name);
                    dict = dictedList.get(0);
                }else {
                    logger.info("物品：{}，新增字典",name);
                    dict = new MaterialDict();
                    dict.setDictName(name);
                    dict.setModName(modName);
                    dict.setWeight(1.0);
                    dict.setPinyin(PinYinUtil.pinYintrasnlate(name));
                }
                materialSet.add(name+modName);
                if(GadgetsConstant.STR_TYPE_CONSTANT.BOOLEAN_FLASE_STR.equals(element.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_ORI))){
                    // 如果物品字典不存在，则保存当前字典，否则只需要将字典加入到列表当中
                    if(!dictExistFlag) {
                        // 非原始物品
                        dict.setOriFlag(GadgetsConstant.NUMBER_CONSTANT.STR_NUMBER_0);
                        dictRepository.saveMaterialDict(dict);
                    }
                    unOriMaterialList.add(element);
                }else if(!dictExistFlag){
                    // 原始物品
                    dict.setOriFlag(GadgetsConstant.NUMBER_CONSTANT.STR_NUMBER_1);
                    dictRepository.saveMaterialDict(dict);
                }
                // 因为后面需要用缓存字段直接获取数据，所以这个阶段就先加载一次字典
                gadgetsBm02Utils.addMcMeterialDictItem(dict);
            }
        }
    }

    /**
     * 合成表解析
     */
    protected void analysisRecipe(){
        RecipeQueryDTO queryDTO = new RecipeQueryDTO();
        for (Element unOriMaterElement : unOriMaterialList) {
            // 获取合成物名称
            String name = unOriMaterElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_NAME);
            String mod = unOriMaterElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_MATERIAL_MOD);
            // 解析合成表
            List<Element> recipeElements = unOriMaterElement.elements();
            if(null!=recipeElements){
                int i = 0;
                // 可能有多个合成表，因此需要遍历
                for (Element recipeElement : recipeElements) {
                    String batchNo = name + i;
                    i++;
                    // 保存物品合成表信息
                    saveRecipe(queryDTO, name, mod, recipeElement, batchNo);
                }
            }
        }
    }

    /**
     * 保存物品合成表信息
     * @param queryDTO          查重条件对象
     * @param name              合成物名称
     * @param targetMod         合成物所属MOD
     * @param recipeElement     合成表节点信息
     * @param batchNo           合成表组合号
     */
    protected void saveRecipe(RecipeQueryDTO queryDTO, String name, String targetMod, Element recipeElement, String batchNo) {
        queryDTO.setBatchNo(batchNo);
        // 查询当前合成表是否已存在
        List<MaterialRecipe> recipes = recipeRepository.selectMaterialRecipeByCondition(queryDTO);
        if(null!=recipes && recipes.size()>0){
            logger.trace("合成表：{} 已经存在，跳过保存", batchNo);
            return;
        }
        logger.info("合成表：{} 不存在，程序保存合成表", batchNo);
        // 解析合成表
        int productUnit = Integer.parseInt(recipeElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_PRODUCT_UNIT,GadgetsConstant.NUMBER_CONSTANT.STR_NUMBER_0));
        if(0==productUnit){
            throw new GadgetsValidateException("物品"+ name +"");
        }
        // 优先读取预设配置
        double energyNum = getEnergyNum(name, recipeElement);
        List<Element> recipeItems = recipeElement.elements();
        if(null==recipeItems || recipeItems.size()==0){
            throw new GadgetsValidateException("物品"+ name +"合成表内部元素不允许为空");
        }
        for (Element recipeItem : recipeItems) {
            // 组装并保存合成表项目信息
            saveRecipeItem(name, targetMod, batchNo, productUnit, energyNum, recipeItem);
        }
    }

    /**
     * 获取合成耗能（EU标准单位）
     * @param name              合成物名称
     * @param recipeElement     合成表节点信息
     * @return                  耗能量（EU标准单位）
     */
    protected double getEnergyNum(String name, Element recipeElement) {
        double energyNum = GadgetsBm02Constant.ENERGY_NUM_TYPE.valueOf(recipeElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_ENERY_NUM_TYPE,"BLANK")).getEnergyNum();
        String energyType = recipeElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_ENERGY_TYPE);
        if(0==energyNum){
            energyNum = Double.parseDouble(recipeElement.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_RECIPE_ENERGY_NUM,GadgetsConstant.NUMBER_CONSTANT.STR_NUMBER_0));
        }
        if(0==energyNum){
            // 如果无需耗能，直接返回即可
            return 0;
        }
        // 将能源换算成EU单位
        if(0!=energyNum && StringUtils.isBlank(energyType)){
            throw new GadgetsValidateException("物品："+ name +"——合成耗能时能源种类不允许为空");
        }
        energyNum = GadgetsBm02Constant.ENERGY_TRANSLATE_TYPE.valueOf(energyType).getRate()*energyNum;
        return energyNum;
    }

    /**
     * 保存合成表项目信息
     * @param name              合成物名称
     * @param batchNo           合成表组合号
     * @param targetMod         合成物所属MOD
     * @param productUnit       合成单位产量
     * @param energyNum         耗能量（EU标准单位）
     * @param recipeItem        合成表项目节点信息
     */
    protected void saveRecipeItem(String name, String targetMod, String batchNo, int productUnit, double energyNum, Element recipeItem) {
        // 校验合成原料信息
        String materialName = recipeItem.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_ITEM_NAME);
        String modName = recipeItem.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_ITEM_MOD);
        double demandNum = Double.parseDouble(recipeItem.attributeValue(GadgetsBm02Constant.MATERIAL_CONFIG.ATTR_ITEM_DEMAND_NUM, GadgetsConstant.NUMBER_CONSTANT.STR_NUMBER_0));
        if (StringUtils.isBlank(materialName)) {
            throw new GadgetsValidateException("合成原料物品名不允许为空");
        }
        if(!materialSet.contains(materialName+modName)){
            throw new GadgetsValidateException("合成原料物品{}不存在",materialName);
        }
        if (0 == demandNum) {
            throw new GadgetsValidateException("合成原料物品的需求数量不允许为空或为0");
        }
        // 组装合成表信息
        MaterialRecipe materialRecipe = new MaterialRecipe();
        materialRecipe.setMaterialName(materialName);
        // 根据名称查询物品
        materialRecipe.setMateriaId(gadgetsBm02Utils.getMcMeterialReDict(materialName+modName));
        materialRecipe.setTargetMeterialName(name);
        materialRecipe.setTargetMeterialId(gadgetsBm02Utils.getMcMeterialReDict(name+targetMod));
        materialRecipe.setDemandNum(demandNum);
        materialRecipe.setGoodNum(productUnit);
        materialRecipe.setBatchNo(batchNo);
        materialRecipe.setEnergyNum(energyNum);
        try {
            // 保存合成表
            recipeRepository.insertRecipe(materialRecipe);
        } catch (Exception exception) {
            logger.error(StringUtils.formatExceptionStack(exception));
        }
    }

}
